#ifndef RESOURCEMANAGER_H
#define RESOURCEMANAGER_H

#include "Resource.h"
#include "Log.h"
#include "Exception.h"

namespace Cvekas {

/// Base class for all resource managers
template <typename T>
class ResourceManager : public boost::noncopyable
{
public:
	/// Constructor
	ResourceManager()
		:
	id_counter(0)
	{
	}
	
	/// Destructor
	virtual ~ResourceManager()
	{
	}

	/// Removes resource
	/// \param name Resource name
	void remove(const std::string& name)
	{
		dictionary_itr = dictionary.find(name);
		if(dictionary_itr == dictionary.end())
			throw EXCEPTION(FORMAT("Resource %s doesn't exist", % name));
		resources[static_cast<int>(dictionary_itr->second)] = T();
		dictionary.erase(dictionary_itr);

		LOG(FORMAT("Resource %s removed", % name));
	}

	/// Removes resource
	/// \param name Resource id
	void remove(ResourceId id)
	{
		std::string name = get(id)->getName();
		dictionary_itr = dictionary.find(name);
		if(dictionary_itr == dictionary.end())
			throw EXCEPTION(FORMAT("Resource %s doesn't exist", % name));
		resources[static_cast<int>(dictionary_itr->second)] = T();
		dictionary.erase(dictionary_itr);

		LOG(FORMAT("Resource %s removed", % name));
	}
	
	/// Returns existing resource
	/// \param name Resource name
	/// \remarks Use get(ResourceId id) if possible, it provides better performace.
	T get(const std::string& name)
	{
		return resources[static_cast<int>(getResourceId(name))];
	}

	/// Returns existing resource
	/// \param id Resource id
	T get(ResourceId id)
	{
		return resources[static_cast<int>(id)];
	}

	/// Returns true if resource with provided name exists.
	bool exists(const std::string& name)
	{
		dictionary_itr = dictionary.find(name);
		if(dictionary_itr == dictionary.end())
			return false;

		return true;
	}

	/// Returns true if resuorce with provided id exists.
	bool exists(ResourceId id)
	{
		if(id < resources.size())
			return true;
	}

protected:
	
	/// Inserts new resource. Must be called after each getUniqueId(const std::string& name) call.
	/// \param name Resource's name
	/// \param resource %Resource
	T insert(const std::string& name, T resource, ResourceId id)
	{
		if(id < resources.size())
			resources[id] = resource;
		else if(id == resources.size())
			resources.push_back(resource);
		else
			throw EXCEPTION("Bad ID provided to ResourceManager::insert");
		return resource;
	}
	
	/// Returns unique id for given name. Throws Exception if such name already exists.
	/// \param name Resource's name
	ResourceId getUniqueId(const std::string& name)
	{
		dictionary_itr = dictionary.find(name);
		if(dictionary_itr != dictionary.end())
			throw EXCEPTION(FORMAT("Resource %s already exists", % name));

		T empty;
		ResourceId new_id = 0;
		while(new_id < resources.size())
		{
			if(resources[new_id++] == empty)
				break;
		}
		if(new_id != resources.size())
			new_id--;

		dictionary.insert(std::make_pair(name, new_id));
		return new_id;
	}

	/// Returns id for existing resource. Throws Exception if there is no such name.
	/// \param name Resource's name
	ResourceId getResourceId(const std::string& name)
	{
		dictionary_itr = dictionary.find(name);
		if(dictionary_itr == dictionary.end())
			throw EXCEPTION(FORMAT("Resource %s doesn't exist", % name));

		return dictionary_itr->second;
	}
	
	std::vector<T> resources;
	typename std::vector<T>::iterator resources_itr;

private:
	ResourceId id_counter;
	
	std::map<std::string, ResourceId> dictionary;
	std::map<std::string, ResourceId>::iterator dictionary_itr;	
};

} // namespace

#endif